using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Web.Script.Serialization;

namespace JWT
{
	public static class JsonWebToken
	{
		private static Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>> HashAlgorithms;

		private static JavaScriptSerializer jsonSerializer;

		static JsonWebToken()
		{
			jsonSerializer = new JavaScriptSerializer();
			HashAlgorithms = new Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>>
			{
				{
					JwtHashAlgorithm.HS256,
					(Func<byte[], byte[], byte[]>)delegate(byte[] key, byte[] value)
					{
						using (HMACSHA256 hMACSHA3 = new HMACSHA256(key))
						{
							return hMACSHA3.ComputeHash(value);
						}
					}
				},
				{
					JwtHashAlgorithm.HS384,
					(Func<byte[], byte[], byte[]>)delegate(byte[] key, byte[] value)
					{
						using (HMACSHA384 hMACSHA2 = new HMACSHA384(key))
						{
							return hMACSHA2.ComputeHash(value);
						}
					}
				},
				{
					JwtHashAlgorithm.HS512,
					(Func<byte[], byte[], byte[]>)delegate(byte[] key, byte[] value)
					{
						using (HMACSHA512 hMACSHA = new HMACSHA512(key))
						{
							return hMACSHA.ComputeHash(value);
						}
					}
				}
			};
		}

		public static string Encode(object payload, byte[] key, JwtHashAlgorithm algorithm, string RandomKey)
		{
			List<string> list = new List<string>();
			var obj = new
			{
				typ = "JWT",
				alg = algorithm.ToString()
			};
			byte[] bytes = Encoding.UTF8.GetBytes(jsonSerializer.Serialize(obj));
			byte[] bytes2 = Encoding.UTF8.GetBytes(jsonSerializer.Serialize(payload));
            //byte[] bytesCode = Encoding.UTF8.GetBytes("222bbb");
            list.Add(Base64UrlEncode(bytes));
			list.Add(Base64UrlEncode(bytes2));
            //list.Add(Base64UrlEncode(bytesCode));
            string s = string.Join(".", list.ToArray());
			byte[] bytes3 = Encoding.UTF8.GetBytes(s + RandomKey);
			byte[] input = HashAlgorithms[algorithm](key, bytes3);
			list.Add(Base64UrlEncode(input));
			return string.Join(".", list.ToArray());
		}

		public static string Encode(object payload, string key, JwtHashAlgorithm algorithm, string RandomKey)
		{
			return Encode(payload, Encoding.UTF8.GetBytes(key), algorithm, RandomKey);
		}

		public static string Decode(string token, byte[] key, string RandomKey, bool verify = true)
		{
			string[] array = token.Split('.');
			string text = array[0];
			string text2 = array[1];
			byte[] inArray = Base64UrlDecode(array[2]);
			string @string = Encoding.UTF8.GetString(Base64UrlDecode(text));
			Dictionary<string, object> dictionary = jsonSerializer.Deserialize<Dictionary<string, object>>(@string);
			string string2 = Encoding.UTF8.GetString(Base64UrlDecode(text2));
			if (verify)
			{
				byte[] bytes = Encoding.UTF8.GetBytes(text + "." + text2+ RandomKey);
				string algorithm = (string)dictionary["alg"];
				byte[] inArray2 = HashAlgorithms[GetHashAlgorithm(algorithm)](key, bytes);
				string text3 = Convert.ToBase64String(inArray);
				string text4 = Convert.ToBase64String(inArray2);
				if (text3 != text4)
				{
					throw new SignatureVerificationException($"Invalid signature. Expected {text3} got {text4}");
				}
			}
			return string2;
		}

		public static string Decode(string token, string key, string RandomKey, bool verify = true)
		{
			return Decode(token, Encoding.UTF8.GetBytes(key), RandomKey, verify);
		}

		public static object DecodeToObject(string token, string key, string RandomKey, bool verify = true)
		{
			string input = Decode(token, key, RandomKey, verify);
			return jsonSerializer.Deserialize<Dictionary<string, object>>(input);
		}

        public static bool VerificationToken(string token, byte[] key, string RandomKey)
        {
            if(string.IsNullOrWhiteSpace(token)) return false;
            string[] array = token.Split('.');
            string text = array[0];
            string text2 = array[1];
            byte[] inArray = Base64UrlDecode(array[2]);
            string @string = Encoding.UTF8.GetString(Base64UrlDecode(text));
            Dictionary<string, object> dictionary = jsonSerializer.Deserialize<Dictionary<string, object>>(@string);
            string string2 = Encoding.UTF8.GetString(Base64UrlDecode(text2));

            byte[] bytes = Encoding.UTF8.GetBytes(text + "." + text2 + RandomKey);
            string algorithm = (string)dictionary["alg"];
            byte[] inArray2 = HashAlgorithms[GetHashAlgorithm(algorithm)](key, bytes);
            string text3 = Convert.ToBase64String(inArray);
            string text4 = Convert.ToBase64String(inArray2);
            if (text3 == text4)
            {
                Dictionary<string, object> jcz_Claims = jsonSerializer.Deserialize<Dictionary<string, object>>(string2);
                DateTime exp =(DateTime)jcz_Claims["exp"];
                DateTime iat = (DateTime)jcz_Claims["iat"];
                DateTime now = DateTime.UtcNow;
                if (DateTime.Compare(now, iat) > 0 && DateTime.Compare(exp, now) > 0)
                    return true;
                else
                    return false;
            }

            return false;
        }

        private static JwtHashAlgorithm GetHashAlgorithm(string algorithm)
		{
			switch (algorithm)
			{
			case "HS256":
				return JwtHashAlgorithm.HS256;
			case "HS384":
				return JwtHashAlgorithm.HS384;
			case "HS512":
				return JwtHashAlgorithm.HS512;
			default:
				throw new SignatureVerificationException("Algorithm not supported.");
			}
		}

		public static string Base64UrlEncode(byte[] input)
		{
			string text = Convert.ToBase64String(input);
			text = text.Split('=')[0];
			text = text.Replace('+', '-');
			return text.Replace('/', '_');
		}

		public static byte[] Base64UrlDecode(string input)
		{
			string text = input.Replace('-', '+');
			text = text.Replace('_', '/');
			switch (text.Length % 4)
			{
			case 2:
				text += "==";
				break;
			case 3:
				text += "=";
				break;
			default:
				throw new Exception("Illegal base64url string!");
			case 0:
				break;
			}
			return Convert.FromBase64String(text);
		}
	}
}
